#!/usr/bin/env python

import math

class SegY(object):
    '''SeqY Class'''
    
    _path = None
    _handler = None
    _data = []
    
    def __init__(self, path):
        self._path = path
    
    def save(self):
        self._read()
        path = self._path.replace('.txt', '.sgy')
        self._handler = open(path, 'wb')
        self._addTextHeader()
        self._addBinaryHeader()
        self._addTraceData()
        self._handler.close()
    
    def _read(self):
        self._data = []
        with open(self._path, 'r') as handler:
            for line in handler:
                if 'Sampling Rate (ms)' in line:
                    data = [x.strip() for x in line.split(':')]
                    self._samplingRate = float(data[1])
                elif 'Sample Number' in line:
                    data = [x.strip() for x in line.split(':')]
                    self._sampleNumber = int(data[1])
                elif 'Gain' in line:
                    data = [x.strip() for x in line.split('\t')]
                    self._gains = []
                    for i in range(1, 25):
                        self._gains.append(int(data[i]))
                elif 'Max' in line:
                    data = [x.strip() for x in line.split('\t')]
                    self._mins = []
                    self._maxes = []
                    self._gains = []
                    for i in range(1, 25):
                        value = int(data[i])
                        self._mins.append(-value)
                        self._maxes.append(value)
                elif 'Data' in line:
                    data = [x.strip() for x in line.split('\t')]
                    datum = []
                    for i in range(1, 25):
                        datum.append(int(data[i]))
                    self._data.append(datum)
        
    def _addTextHeader(self):
        s = ''
        s += "C 1 CLIENT                        COMPANY                       CREW NO         "
        s += "C 2 LINE            AREA                        MAP ID                          "
        s += "C 3 REEL NO           DAY-START OF REEL     TEAR      OBSERVER                  "
        s += "C 4 INSTRUMENT: MPG            MODEL            SERIAL NO                       "
        s += "C 5 DATA TRACES/RECORD        AUXILIARY TRACES/RECORD         CDP FOLD          "
        s += "C 6 SAMPLE INTERNAL         SAMPLES/TRACE       BITS/IN      BYTES/SAMPLE       "
        s += "C 7 RECORDING FORMAT        FORMAT THIS REEL        MEASUREMENT SYSTEM          "
        s += "C 8 SAMPLE CODE: FLOATING PT     FIXED PT     FIXED PT-GAIN     CORRELATED      "
        s += "C 9 GAIN  TYPE: FIXED     BINARY     FLOATING POINT     OTHER                   "
        s += "C10 FILTERS: ALIAS     HZ  NOTCH     HZ  BAND     -     HZ  SLOPE    -    DB/OCT"
        s += "C11 SOURCE: TYPE            NUMBER/POINT        POINT INTERVAL                  "
        s += "C12     PATTERN:                           LENGTH        WIDTH                  "
        s += "C13 SWEEP: START     HZ  END     HZ  LENGTH      MS  CHANNEL NO     TYPE        "
        s += "C14 TAPER: START LENGTH       MS  END LENGTH       MS  TYPE                     "
        s += "C15 SPREAD: OFFSET        MAX DISTANCE        GROUP INTERVAL                    "
        s += "C16 GEOPHONES: PER GROUP     SPACING     FREQEUNCY     MPG                      "
        s += "C17     PATTERN:                           LENGTH        WIDTH                  "
        s += "C18 TRACES SORTED BY: RECORD     CDP     OTHER                                  "
        s += "C19 AMPLITUDE RECOVERY: NONE      SPHERICAL DIV       AGC    OTHER              "
        s += "C20 MAP PROJECTION                      ZONE ID       COORDINATE UNIT           "
        s += "C21 PROCESSING:                                                                 "
        s += "C22 PROCESSING:                                                                 "
        s += "C23                                                                             "
        s += "C24                                                                             "
        s += "C25                                                                             "
        s += "C26                                                                             "
        s += "C27                                                                             "
        s += "C28                                                                             "
        s += "C29                                                                             "
        s += "C30                                                                             "
        s += "C31                                                                             "
        s += "C32                                                                             "
        s += "C33                                                                             "
        s += "C34                                                                             "
        s += "C35                                                                             "
        s += "C36                                                                             "
        s += "C37                                                                             "
        s += "C38                                                                             "
        s += "C39                                                                             "
        s += "C40 END EBCDIC                                                                  "
        s = s.replace(" ", '\x00')
        self._handler.write(s)
    
    def _addBinaryHeader(self):
        for pos in range(3201, 3601):
            if pos == 3204:
                # Job number
                value = 1
            elif pos == 3208:
                # Line number
                value = 1
            elif pos == 3212:
                # Reel number
                value = 1
            elif pos == 3214:
                # Number of data trace per record
                value = 24
            elif pos == 3217:
                # Sample interval (microsecond)
                value = int((self._samplingRate * 1000) / 256)
            elif pos == 3218:
                # Sample interval (microsecond)
                value = int((self._samplingRate * 1000) % 256)
            elif pos == 3221:
                # Number of sample per data trace
                value = int(self._sampleNumber / 256)
            elif pos == 3222:
                # Number of sample per data trace
                value = int(self._sampleNumber % 256)
            elif pos == 3226:
                # Data sample format
                # 3 means int16
                value = 3
            else:
                value = 0
            self._handler.seek(pos - 1)
            self._handler.write(chr(value))
            
    def _addTraceData(self):
        maxValue = (2 ** 16) - 1
        
        pos = 3600 - 1
        for trace in range(24):
            # Trace Header
            for i in range(1, 241):
                if i == 4:
                    # Trace sequence number within line
                    value = trace + 1
                elif i == 12:
                    # Original field record number
                    value = 1
                elif i == 16:
                    # Trace number within the original field record
                    value = trace + 1
                elif i == 30:
                    # Trace identification code
                    value = 1
                elif i == 115:
                    # Number of sample
                    value = int(self._sampleNumber / 256)
                elif i == 116:
                    # Number of sample
                    value = int(self._sampleNumber % 256)
                elif i == 117:
                    # Sample interval (microsecond)
                    value = int((self._samplingRate * 1000) / 256)
                elif i == 118:
                    # Sample interval (microsecond)
                    value = int((self._samplingRate * 1000) % 256)
                elif i == 120:
                    # Gain type
                    value = 1
                elif i == 124:
                    # Gain
                    value = self._maxes[trace]
                    value = abs(int(-20 * (math.log10(50 * (10 / value)))))
                else:
                    value = 0
                pos += 1
                self._handler.seek(pos)
                self._handler.write(chr(value))
            
            for i in range(self._sampleNumber):
                value = self._data[i][trace]
                if value < 0:
                    value += maxValue
                pos += 1
                self._handler.seek(pos)
                self._handler.write(chr(int(value / 256)))
                pos += 1
                self._handler.seek(pos)
                self._handler.write(chr(int(value % 256)))
                
if __name__ == '__main__':
    segy = SegY(r'D:\Geophysics\Louise\data\Bandung [2012-12-14 22-28-13].txt')
    segy.save()
##    with open('D:\\text.bin', 'wb') as handler:
##        handler.write(chr(65000 / 256))
##        handler.write(chr(65000 % 256))
    